<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width"/>

<meta name="theme-color" content="#414852"/>


<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inconsolata|Itim|Lobster.css">
<link rel="icon" type="image/png" sizes="64x64" href="/images/favicon/xin-64.png" /><link rel="icon" type="image/png" sizes="128x128" href="/images/favicon/xin-128.png" /><link rel="apple-touch-icon" sizes="128x128" href="/images/favicon/xin-128.png" /><link rel="mask-icon" color="white" href="/images/favicon/xin.svg" />  <meta name="description" content="这是一篇介绍Spring Cloud Contract语言定义的文章，也就是该怎么写契约内容。如果您对Spring Cloud Contract不是很了解，不知如何更好的实践的话，可以先看下我之前的文章《Spring Cloud Contract 契约测试》 在这个框架中，我们既可以采用Groovy，也可以yaml。但由于本身属于Java的框架，在支持上Groovy要更好些，推荐且这里只介绍Gro">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring Cloud Contract DSL">
<meta property="og:url" content="https://jiangtj.com/articles/beechnut/spring-cloud-contract-dsl/index.html">
<meta property="og:site_name" content="Dnocm ☀">
<meta property="og:description" content="这是一篇介绍Spring Cloud Contract语言定义的文章，也就是该怎么写契约内容。如果您对Spring Cloud Contract不是很了解，不知如何更好的实践的话，可以先看下我之前的文章《Spring Cloud Contract 契约测试》 在这个框架中，我们既可以采用Groovy，也可以yaml。但由于本身属于Java的框架，在支持上Groovy要更好些，推荐且这里只介绍Gro">
<meta property="og:locale">
<meta property="article:published_time" content="2019-04-28T13:29:12.000Z">
<meta property="article:modified_time" content="2019-04-28T13:29:12.000Z">
<meta property="article:author" content="Mr.J">
<meta property="article:tag" content="Spring Cloud">
<meta property="article:tag" content="微服务">
<meta name="twitter:card" content="summary">  <title>Spring Cloud Contract DSL | Dnocm ☀</title>    <script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","mainEntityOfPage":{"@type":"WebPage","@id":"https://jiangtj.com/articles/beechnut/spring-cloud-contract-dsl/"},"headline":"Spring Cloud Contract DSL","image":[],"datePublished":"2019-04-28T13:29:12.000Z","dateModified":"2019-04-28T13:29:12.000Z","author":{"@type":"Person","name":"Mr.J"},"publisher":{"@type":"Organization","name":"Mr.J","logo":{"@type":"ImageObject","url":"https://jiangtj.com/images/avatar.jpg"}}}</script><script data-ad-client="ca-pub-8243423215749776" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<meta name="google-site-verification" content="TWf4Ma3V7xRcvKFWw_1iOCJKgNJmUUB1CmLucKjvV2A" />
<meta name="baidu-site-verification" content="xtf2YgRPUK" />

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-132230328-1"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-132230328-1');
</script>
<link rel="stylesheet" type="text/css" href="/css/injector/main.css" /><link rel="preload" as="style" href="/css/injector/dark.css" /><link rel="preload" as="style" href="/css/injector/light.css" /><link rel="preload" as="style" href="/css/injector/auto.css" /><!-- hexo injector head_end start --><link rel="manifest" href="/manifest.json" /><!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/rss2.xml" title="Dnocm ☀" type="application/rss+xml">
</head>

<body>

    
  <header class="main-header">
    <div class="header-inner card"><div class="brand">
  <div class="site-meta">
    <div class="custom-logo-site-title">
      <a href="/">
        <span class="site-title">Dnocm ☀</span>
      </a>
    </div>
    
  </div>

  <div class="site-nav-toggle">
    <button>
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path></svg>    </button>
  </div>
</div>
<nav class="site-nav">
  <ul class="menu">
    <li class="menu-item menu-item-home"><a href="/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="home" class="svg-inline--fa fa-home fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path></svg>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="tags" class="svg-inline--fa fa-tags fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"></path></svg>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="th" class="svg-inline--fa fa-th fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z"></path></svg>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="archive" class="svg-inline--fa fa-archive fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 448c0 17.7 14.3 32 32 32h384c17.7 0 32-14.3 32-32V160H32v288zm160-212c0-6.6 5.4-12 12-12h104c6.6 0 12 5.4 12 12v8c0 6.6-5.4 12-12 12H204c-6.6 0-12-5.4-12-12v-8zM480 32H32C14.3 32 0 46.3 0 64v48c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16V64c0-17.7-14.3-32-32-32z"></path></svg>归档</a></li><li class="menu-item menu-item-more"><a href="/more/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="info-circle" class="svg-inline--fa fa-info-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>More</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg>搜索</a></li>  </ul>
</nav>
</div>
  </header>

  <div class="flex-container">

    

<aside class="sidebar">

  <div class="header-inner card">
    <div class="brand">
  <div class="site-meta">
    <div class="custom-logo-site-title">
      <a href="/">
        <span class="site-title">Dnocm ☀</span>
      </a>
    </div>
    
  </div>

  <div class="site-nav-toggle">
    <button>
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path></svg>    </button>
  </div>
</div>
<nav class="site-nav">
  <ul class="menu">
    <li class="menu-item menu-item-home"><a href="/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="home" class="svg-inline--fa fa-home fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path></svg>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="tags" class="svg-inline--fa fa-tags fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"></path></svg>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="th" class="svg-inline--fa fa-th fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z"></path></svg>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="archive" class="svg-inline--fa fa-archive fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 448c0 17.7 14.3 32 32 32h384c17.7 0 32-14.3 32-32V160H32v288zm160-212c0-6.6 5.4-12 12-12h104c6.6 0 12 5.4 12 12v8c0 6.6-5.4 12-12 12H204c-6.6 0-12-5.4-12-12v-8zM480 32H32C14.3 32 0 46.3 0 64v48c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16V64c0-17.7-14.3-32-32-32z"></path></svg>归档</a></li><li class="menu-item menu-item-more"><a href="/more/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="info-circle" class="svg-inline--fa fa-info-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>More</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg>搜索</a></li>  </ul>
</nav>
  </div>

  <div class="sidebar-inner card">

    

    <ul class="sidebar-nav">
      <li class="sidebar-nav-toc sidebar-nav-active">
        文章目录
      </li>
      <li class="sidebar-nav-overview">
        站点概览
      </li>
    </ul>
    <div class="post-toc-wrap sidebar-panel sidebar-panel-active">
      <div class="post-toc"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E9%A1%B6%E7%BA%A7%E5%85%83%E7%B4%A0"><span class="nav-number">1.</span> <span class="nav-text">顶级元素</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Name"><span class="nav-number">1.1.</span> <span class="nav-text">Name</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Description"><span class="nav-number">1.2.</span> <span class="nav-text">Description</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Ignored"><span class="nav-number">1.3.</span> <span class="nav-text">Ignored</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Priority"><span class="nav-number">1.4.</span> <span class="nav-text">Priority</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#HTTP%E5%85%83%E7%B4%A0"><span class="nav-number">1.5.</span> <span class="nav-text">HTTP元素</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Request"><span class="nav-number">2.</span> <span class="nav-text">Request</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Method"><span class="nav-number">2.1.</span> <span class="nav-text">Method</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Url"><span class="nav-number">2.2.</span> <span class="nav-text">Url</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A8%E6%80%81%E5%8F%82%E6%95%B0"><span class="nav-number">2.2.1.</span> <span class="nav-text">动态参数</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Header"><span class="nav-number">2.3.</span> <span class="nav-text">Header</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Body"><span class="nav-number">2.4.</span> <span class="nav-text">Body</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Response"><span class="nav-number">3.</span> <span class="nav-text">Response</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Status"><span class="nav-number">3.1.</span> <span class="nav-text">Status</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#FromReques"><span class="nav-number">3.2.</span> <span class="nav-text">FromReques</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Body-1"><span class="nav-number">3.3.</span> <span class="nav-text">Body</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E4%B8%AA%E6%AF%94%E8%BE%83%E5%AE%8C%E6%95%B4%E7%A4%BA%E4%BE%8B"><span class="nav-number">4.</span> <span class="nav-text">一个比较完整示例</span></a></li></ol></div>
    </div>
    <div class="site-overview-wrap sidebar-panel">
      <div class="site-overview">
  <div class="site-author">
    
    <img class="site-author-image" src="/images/avatar.jpg"
      alt="Mr.J" />
    
    <p class="site-author-name">Mr.J</p>
    <div class="site-description">何须更问浮生事，只此浮生是梦中</div>
  </div>

  <nav class="site-state">
    
    <div class="site-state-item site-state-posts">
      <a href="/archives/">
        <span class="site-state-item-count">64</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    

    
    <div class="site-state-item site-state-categories">
      <a href="/categories/">
        <span class="site-state-item-count">8</span>
        <span class="site-state-item-name">分类</span>
      </a>
    </div>
    

    
    <div class="site-state-item site-state-tags">
      <a href="/tags/">
        <span class="site-state-item-count">48</span>
        <span class="site-state-item-name">标签</span>
      </a>
    </div>
    
  </nav>

  <div class="social-links"><a href="mailto:i@dnocm.com" title="mailto:i@dnocm.com" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="envelope" class="svg-inline--fa fa-envelope fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"></path></svg></a><a href="https://t.me/dnocm" title="https://t.me/dnocm" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="telegram" class="svg-inline--fa fa-telegram fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm121.8 169.9l-40.7 191.8c-3 13.6-11.1 16.9-22.4 10.5l-62-45.7-29.9 28.8c-3.3 3.3-6.1 6.1-12.5 6.1l4.4-63.1 114.9-103.8c5-4.4-1.1-6.9-7.7-2.5l-142 89.4-61.2-19.1c-13.3-4.2-13.6-13.3 2.8-19.7l239.1-92.2c11.1-4 20.8 2.7 17.2 19.5z"></path></svg></a><a href="/rss2.xml" title="/rss2.xml" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="rss" class="svg-inline--fa fa-rss fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M128.081 415.959c0 35.369-28.672 64.041-64.041 64.041S0 451.328 0 415.959s28.672-64.041 64.041-64.041 64.04 28.673 64.04 64.041zm175.66 47.25c-8.354-154.6-132.185-278.587-286.95-286.95C7.656 175.765 0 183.105 0 192.253v48.069c0 8.415 6.49 15.472 14.887 16.018 111.832 7.284 201.473 96.702 208.772 208.772.547 8.397 7.604 14.887 16.018 14.887h48.069c9.149.001 16.489-7.655 15.995-16.79zm144.249.288C439.596 229.677 251.465 40.445 16.503 32.01 7.473 31.686 0 38.981 0 48.016v48.068c0 8.625 6.835 15.645 15.453 15.999 191.179 7.839 344.627 161.316 352.465 352.465.353 8.618 7.373 15.453 15.999 15.453h48.068c9.034-.001 16.329-7.474 16.005-16.504z"></path></svg></a><a href="https://github.com/JiangTJ" title="https://github.com/JiangTJ" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="svg-inline--fa fa-github fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg></a></div><div class="font-size-ol"><a class="font-size-ol-sub">A-</a><span class="font-size-ol-value">14</span><a class="font-size-ol-plus">A+</a></div>
</div>
    </div>

    

  </div>
</aside>

    <div class="flex-main">

      <main>
        
        <div id="content" class="content">
          <div>
  

<article class="post post-type-normal">
  <div class="card post-block">

    

    <header class="post-header">

      
      <h1 class="post-title">
        
        Spring Cloud Contract DSL
        
      </h1>
      

      <div class="post-meta">

  
  
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="calendar" class="svg-inline--fa fa-calendar fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M400 64h-48V12c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v52H160V12c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v52H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zm-6 400H54c-3.3 0-6-2.7-6-6V160h352v298c0 3.3-2.7 6-6 6z"></path></svg>
    </span>
    <span class="post-meta-item-text">发表于</span>
    <time title="创建时间：2019-04-28 21:29:12">2019-04-28</time>
  </span>
  

  

  
  
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="folder" class="svg-inline--fa fa-folder fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 128H272l-64-64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V176c0-26.51-21.49-48-48-48z"></path></svg>
    </span>
    <span class="post-meta-item-text">分类于</span>
    <span><a href="/categories/%E5%90%8E%E7%AB%AF/" rel="index"><span>后端</span></a></span>  </span>
  

  
  

</div>

    </header>

    <div class="post-body">
      
      <p>这是一篇介绍Spring Cloud Contract语言定义的文章，也就是该怎么写契约内容。如果您对Spring Cloud Contract不是很了解，不知如何更好的实践的话，可以先看下我之前的文章<a href="/articles/beechnut/spring-cloud-contract">《Spring Cloud Contract 契约测试》</a></p>
<p>在这个框架中，我们既可以采用Groovy，也可以yaml。但由于本身属于Java的框架，在支持上Groovy要更好些，推荐且这里只介绍Groovy（事实上，我对Spring官方同时支持两种定义方式并不理解，专注一种或许会更好啊）。</p>
<blockquote>
<p>该文章基于Spring Cloud Contract 2.1.0.GA</p>
</blockquote>
<span id="more"></span>

<h1 id="顶级元素" class="article-heading"><a href="#顶级元素" class="headerlink" title="顶级元素"></a>顶级元素<a class="article-anchor" href="#顶级元素" aria-hidden="true"></a></h1><p>首先，使用Groovy做契约脚本，必须使用<code>org.springframework.cloud.spec.Contract.make</code>，当然，也可以选择<code>import</code>，如下</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs groovy"><span class="hljs-keyword">package</span> contracts<br><br><span class="hljs-keyword">import</span> org.springframework.cloud.contract.spec.Contract<br><br><span class="hljs-comment">//定义单个契约</span><br>Contract.make &#123;&#125;<br><br><span class="hljs-comment">//如果需要有多个存在一个文件中</span><br>[<br>    Contract.make &#123;&#125;,<br>    Contract.make &#123;&#125;<br>]<br></code></pre></td></tr></table></figure>

<p>这里将make{}中直接使用的方法或者属性成为顶级元素。在脚本中，不会特别区分方法还是属性的，所以用元素来代替。</p>
<p>在Spring Cloud Contract中有以下几种顶级元素</p>
<ul>
<li>name: 名称</li>
<li>description: 描述</li>
<li>ignored: 忽略</li>
<li>priority: 优先级</li>
<li>HTTP元素</li>
</ul>
<h2 id="Name" class="article-heading"><a href="#Name" class="headerlink" title="Name"></a>Name<a class="article-anchor" href="#Name" aria-hidden="true"></a></h2><p>在定义一个文件中多个契约时，默认会使用<code>-[index]</code>后缀来区分不同的契约，这样的测试用例可读性较差（不清楚哪个是做什么的）。当然，单个可能也会遇到文件名称无法表达的情况。所以我们需要通过<code>name()</code>来修改生成的测试用例名称，当然它也会同时修改<code>WireMock stub</code>（指契约mock时，所使用的json文件）名称。</p>
<p>例如：</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    name(<span class="hljs-string">&#x27;第一个契约呀&#x27;</span>)<br>&#125;<br></code></pre></td></tr></table></figure>

<p>结果：测试用例</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> validate_第一个契约呀() <span class="hljs-keyword">throws</span> Exception &#123;&#125;<br></code></pre></td></tr></table></figure>

<p>结果：WireMock stub</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">第一个契约呀.json<br></code></pre></td></tr></table></figure>

<blockquote>
<p>你们不要用中文哦~</p>
</blockquote>
<h2 id="Description" class="article-heading"><a href="#Description" class="headerlink" title="Description"></a>Description<a class="article-anchor" href="#Description" aria-hidden="true"></a></h2><p>描述用途，或者在BDD中所需要描述的角色、想要、目的、场景、限制等等，可以写在这里。它不会生成到单元测试或者存根中，仅仅做记录而已。但，请看下面的例子：</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    description(<span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">        [运营]需要[去掉操作A]，目的是[减少流程与人工成本]，存在的问题[减少A操作，会使部分数据未记录（运营人员已知晓（见邮件‘邮件主题’））]</span><br><span class="hljs-string">    &quot;&quot;&quot;</span>)<br>&#125;<br></code></pre></td></tr></table></figure>

<p>然而某天，由于缺少A操作的记录，导致部分问题无法解决，他们会“趾高气扬”的责备，这程序设计的怎么怎么烂。但如果有这段描述，你就淡定了，喝杯咖啡，查下邮件，转发某某，抄送领导A、领导B。。。（这样的事在业务驱动的公司十分常见）</p>
<h2 id="Ignored" class="article-heading"><a href="#Ignored" class="headerlink" title="Ignored"></a>Ignored<a class="article-anchor" href="#Ignored" aria-hidden="true"></a></h2><p>如果你不希望生成某个契约，你可以在插件<code>&lt;configuration&gt;&lt;includedFiles&gt;</code>中忽略或者在契约内添加<code>ignored()</code></p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    ignored()<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="Priority" class="article-heading"><a href="#Priority" class="headerlink" title="Priority"></a>Priority<a class="article-anchor" href="#Priority" aria-hidden="true"></a></h2><p>有时我们会定义相似的契约，比如当调用接口<code>/user/&#123;id&#125;</code>，id为10时失败，其它都成功。那么我们需要为id为10设置更高的优先级</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs groovy"><span class="hljs-comment">//url与urlPath稍后讲，一个指定值，一个进行正则匹配</span><br>Contract.make &#123;<br>    request &#123;<br>        url <span class="hljs-string">&quot;/user/10&quot;</span><br>        <span class="hljs-comment">//...</span><br>    &#125;<br>    priority(<span class="hljs-number">1</span>)<br>&#125;<br>Contract.make &#123;<br>    request &#123;<br>        urlPath($(c(regex(<span class="hljs-string">&#x27;^/user/.+&#x27;</span>)),p(<span class="hljs-number">1</span>)))<br>        <span class="hljs-comment">//...</span><br>    &#125;<br>    priority(<span class="hljs-number">2</span>)<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="HTTP元素" class="article-heading"><a href="#HTTP元素" class="headerlink" title="HTTP元素"></a>HTTP元素<a class="article-anchor" href="#HTTP元素" aria-hidden="true"></a></h2><p>除了上面的，还包含HTTP元素，即request、response。这两个元素是契约最重要的组成部分，必不可少。在官方文档中priority算在了这里，但我更倾向于分开（我们常说的HTTP请求有优先级？）</p>
<p>http是十分重要的，所以放在单独的两章中</p>
<h1 id="Request" class="article-heading"><a href="#Request" class="headerlink" title="Request"></a>Request<a class="article-anchor" href="#Request" aria-hidden="true"></a></h1><p>一个HTTP Request一般情况由method、url、header、requestBody这几部分组成，HTTP协议中method、url是强制的，同样的在契约里也属于必须提供的内容</p>
<h2 id="Method" class="article-heading"><a href="#Method" class="headerlink" title="Method"></a>Method<a class="article-anchor" href="#Method" aria-hidden="true"></a></h2><p>在Request设置Method属性</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    request &#123;<br>        method <span class="hljs-string">&#x27;PUT&#x27;</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="Url" class="article-heading"><a href="#Url" class="headerlink" title="Url"></a>Url<a class="article-anchor" href="#Url" aria-hidden="true"></a></h2><p>最简单设置url的方式是，直接设置url属性</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    request &#123;<br>        url <span class="hljs-string">&#x27;/user/10&#x27;</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>或者你可以使用urlPath，一个Path组件来定义url</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    request &#123;<br>        urlPath(<span class="hljs-string">&#x27;/users&#x27;</span>) &#123;<br>            queryParameters &#123;<br>                parameter <span class="hljs-string">&#x27;limit&#x27;</span>: <span class="hljs-number">100</span><br>                parameter <span class="hljs-string">&#x27;filter&#x27;</span>: equalTo(<span class="hljs-string">&quot;email&quot;</span>)<br>                parameter <span class="hljs-string">&#x27;gender&#x27;</span>: value(consumer(containing(<span class="hljs-string">&quot;[mf]&quot;</span>)), producer(<span class="hljs-string">&#x27;mf&#x27;</span>))<br>                parameter <span class="hljs-string">&#x27;offset&#x27;</span>: value(consumer(matching(<span class="hljs-string">&quot;[0-9]+&quot;</span>)), producer(<span class="hljs-number">123</span>))<br>                parameter <span class="hljs-string">&#x27;loginStartsWith&#x27;</span>: value(consumer(notMatching(<span class="hljs-string">&quot;.&#123;0,2&#125;&quot;</span>)), producer(<span class="hljs-number">3</span>))<br>                parameter <span class="hljs-string">&#x27;uuid&#x27;</span>: $(anyUuid())<br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>urlPath分为两部分，本身的路径以及参数。路径可以是正则（前面的例子）也可以是想url里那样的普通字符串。</p>
<p>另一部分<code>queryParameters</code>是<code>？</code>后面的参数，其中简单的，如<code>&#39;limit&#39;: 100</code>与<code>&#39;filter&#39;: equalTo(&quot;email&quot;)</code>是一样的，只是写法不同，表示值相等的情况。另外就涉及的动态参数。</p>
<h3 id="动态参数" class="article-heading"><a href="#动态参数" class="headerlink" title="动态参数"></a>动态参数<a class="article-anchor" href="#动态参数" aria-hidden="true"></a></h3><p>除了<code>equalTo</code>我们还看到了<code>value()``$()</code>，这是Spring Cloud Contract另一大特性，动态参数。可以试想一下如果我们调用的参数是指定，那么会怎样呢？</p>
<p>首先，我们无法保证调用方是一个还是多个，假如一个业务需要模拟多用户调用，那么我们需要mock不同参数的数据。在指定参数的情况下意味着我们需要多份契约，以便消费方调用。这显然不合理，多份契约是冗余的，没有启到任何作用，而且每次的修改，需要同时修改多个地方。</p>
<p>所以，我们应当接受任意我们所能接受的数据，并返回预期结果，以方便消费者也可以根据其业务定制他的参数。</p>
<p>在动态参数中，有些写法不同，但实际是一样的，比如：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs js">value() = $()<br>consumer(...) = c(...) = stub(...) = client(...)<br>producer(...) = p(...) = test(...) = server(...)<br></code></pre></td></tr></table></figure>

<p>在动态属性中，基本的结构是<code>$(consumer(...),producer(...))</code>。consumer定义接受的参数，或者消费方的定义。producer表示验证的数据，提供方验证用的数据，也就是单元测试中用到的数据</p>
<p>consumer一般都多种匹配方式，<code>containing</code>包含，<code>matching</code>正则匹配，<code>notMatching</code>正则不匹配。</p>
<p>其中正则匹配，Spring Cloud Contract内置了一些正则表达式，可以直接使用</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br></pre></td><td class="code"><pre><code class="hljs groovy"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern TRUE_OR_FALSE = Pattern.compile(<span class="hljs-regexp">/(true|false)/</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern ALPHA_NUMERIC = Pattern.compile(<span class="hljs-string">&#x27;[a-zA-Z0-9]+&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern ONLY_ALPHA_UNICODE = Pattern.compile(<span class="hljs-regexp">/[\p&#123;L&#125;]*/</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern NUMBER = Pattern.compile(<span class="hljs-string">&#x27;-?(\\d*\\.\\d+|\\d+)&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern INTEGER = Pattern.compile(<span class="hljs-string">&#x27;-?(\\d+)&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern POSITIVE_INT = Pattern.compile(<span class="hljs-string">&#x27;([1-9]\\d*)&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern DOUBLE = Pattern.compile(<span class="hljs-string">&#x27;-?(\\d*\\.\\d+)&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern HEX = Pattern.compile(<span class="hljs-string">&#x27;[a-fA-F0-9]+&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern IP_ADDRESS = Pattern.<br>		compile(<span class="hljs-string">&#x27;([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern HOSTNAME_PATTERN = Pattern.<br>		compile(<span class="hljs-string">&#x27;((http[s]?|ftp):/)/?([^:/\\s]+)(:[0-9]&#123;1,5&#125;)?&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern EMAIL = Pattern.<br>		compile(<span class="hljs-string">&#x27;[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]&#123;2,6&#125;&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern URL = UrlHelper.URL<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern HTTPS_URL = UrlHelper.HTTPS_URL<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern UUID = Pattern.<br>		compile(<span class="hljs-string">&#x27;[a-f0-9]&#123;8&#125;-[a-f0-9]&#123;4&#125;-[a-f0-9]&#123;4&#125;-[a-f0-9]&#123;4&#125;-[a-f0-9]&#123;12&#125;&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern ANY_DATE = Pattern.<br>		compile(<span class="hljs-string">&#x27;(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern ANY_DATE_TIME = Pattern.<br>		compile(<span class="hljs-string">&#x27;([0-9]&#123;4&#125;)-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern ANY_TIME = Pattern.<br>		compile(<span class="hljs-string">&#x27;(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])&#x27;</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern NON_EMPTY = Pattern.compile(<span class="hljs-regexp">/[\S\s]+/</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern NON_BLANK = Pattern.compile(<span class="hljs-regexp">/^\s*\S[\S\s]*/</span>)<br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Pattern ISO8601_WITH_OFFSET = Pattern.<br>		compile(<span class="hljs-regexp">/([0-9]&#123;4&#125;)-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.\d&#123;3&#125;)?(Z|[+-][01]\d:[0-5]\d)/</span>)<br><br><span class="hljs-keyword">protected</span> <span class="hljs-keyword">static</span> Pattern anyOf(String... values) &#123;<br>	<span class="hljs-keyword">return</span> Pattern.compile(values.collect(&#123; <span class="hljs-string">&quot;^$it\$&quot;</span> &#125;).join(<span class="hljs-string">&quot;|&quot;</span>))<br>&#125;<br><br>RegexProperty onlyAlphaUnicode() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(ONLY_ALPHA_UNICODE).asString()<br>&#125;<br><br>RegexProperty alphaNumeric() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(ALPHA_NUMERIC).asString()<br>&#125;<br><br>RegexProperty number() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(NUMBER).asDouble()<br>&#125;<br><br>RegexProperty positiveInt() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(POSITIVE_INT).asInteger()<br>&#125;<br><br>RegexProperty anyBoolean() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(TRUE_OR_FALSE).asBooleanType()<br>&#125;<br><br>RegexProperty anInteger() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(INTEGER).asInteger()<br>&#125;<br><br>RegexProperty aDouble() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(DOUBLE).asDouble()<br>&#125;<br><br>RegexProperty ipAddress() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(IP_ADDRESS).asString()<br>&#125;<br><br>RegexProperty hostname() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(HOSTNAME_PATTERN).asString()<br>&#125;<br><br>RegexProperty email() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(EMAIL).asString()<br>&#125;<br><br>RegexProperty url() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(URL).asString()<br>&#125;<br><br>RegexProperty httpsUrl() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(HTTPS_URL).asString()<br>&#125;<br><br>RegexProperty uuid() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(UUID).asString()<br>&#125;<br><br>RegexProperty isoDate() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(ANY_DATE).asString()<br>&#125;<br><br>RegexProperty isoDateTime() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(ANY_DATE_TIME).asString()<br>&#125;<br><br>RegexProperty isoTime() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(ANY_TIME).asString()<br>&#125;<br><br>RegexProperty iso8601WithOffset() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(ISO8601_WITH_OFFSET).asString()<br>&#125;<br><br>RegexProperty nonEmpty() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(NON_EMPTY).asString()<br>&#125;<br><br>RegexProperty nonBlank() &#123;<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> RegexProperty(NON_BLANK).asString()<br>&#125;<br></code></pre></td></tr></table></figure>

<p>还有，如果你觉得<code>value(consumer(number()), producer(&quot;1&quot;))</code>有点麻烦的话，这个框架还提供了一种简便写法，<code>anyNumber()</code>。任何一个<code>any*</code>都代表一个正则，如果使用这个简便写法，在producer不提供的情况下，会随机生成一个符合的参数，用于接口测试。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs js">value(anyNumber()) = value(consumer(number()), producer(<span class="hljs-string">&quot;一个随机的数字&quot;</span>))<br>value(anyNumber(), producer(<span class="hljs-string">&quot;1&quot;</span>)) = value(consumer(number()), producer(<span class="hljs-string">&quot;1&quot;</span>))<br></code></pre></td></tr></table></figure>

<h2 id="Header" class="article-heading"><a href="#Header" class="headerlink" title="Header"></a>Header<a class="article-anchor" href="#Header" aria-hidden="true"></a></h2><p>用于匹配Request头部是否符合规范</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    request &#123;<br>        headers &#123;<br>            header(<span class="hljs-string">&#x27;contentType&#x27;</span>: <span class="hljs-string">&#x27;application/json&#x27;</span>)<br>            <span class="hljs-comment">//在groovy中可以使用内置的函数代替如下面</span><br>            <span class="hljs-comment">//contentType(applicationJsonUtf8())</span><br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="Body" class="article-heading"><a href="#Body" class="headerlink" title="Body"></a>Body<a class="article-anchor" href="#Body" aria-hidden="true"></a></h2><p>当method为PUT或者POST时，依据http协议，我们可以将数据放在body中。</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    request &#123;<br>        headers &#123;<br>            contentType(applicationJson())<br>        &#125;<br>        body([<br>                <span class="hljs-symbol">name:</span> value(anyNonEmptyString(), producer(<span class="hljs-string">&quot;从入门到弃坑&quot;</span>)),<br>                <span class="hljs-symbol">price:</span> value(anyNumber(), producer(<span class="hljs-string">&quot;1&quot;</span>))<br>        ])<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>根据contentType不同，它会自动转换至Json或者FormParam。body的值也能设置动态参数，参考上文。除此之外，body还支持bodyMatchers，我们可以提供body样例，在外部提供匹配规则</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    request &#123;<br>        headers &#123;<br>            contentType(applicationJson())<br>        &#125;<br>        body ([<br>            <span class="hljs-string">&quot;name&quot;</span>:<span class="hljs-string">&quot;YaYaYa&quot;</span><br>        ])<br>        bodyMatchers &#123;<br>            jsonPath(<span class="hljs-string">&#x27;$.name&#x27;</span>, byRegex(nonBlank()))<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>通过<code>$.[path]</code>获取json位置，通过<code>by*</code>定义匹配规则</p>
<blockquote>
<p>但需要特别注意：动态参数目前还不支持FormParam。如果存在FormParam，改用QueryParam传参（反正一样的）。<br>参考：<a target="_blank" rel="noopener" href="https://github.com/spring-cloud/spring-cloud-contract/issues/112">spring-cloud-contract#112</a>与<a target="_blank" rel="noopener" href="https://github.com/tomakehurst/wiremock/issues/383">wiremock#383</a></p>
</blockquote>
<h1 id="Response" class="article-heading"><a href="#Response" class="headerlink" title="Response"></a>Response<a class="article-anchor" href="#Response" aria-hidden="true"></a></h1><p>response与request存在一些共通点，比如<code>header</code> <code>body</code>等，这些写法上与Request中是一致的，可以参考上文</p>
<h2 id="Status" class="article-heading"><a href="#Status" class="headerlink" title="Status"></a>Status<a class="article-anchor" href="#Status" aria-hidden="true"></a></h2><p><code>status code</code>是响应独有的，而且是必须的一项</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    response &#123;<br>        status OK()<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h2 id="FromReques" class="article-heading"><a href="#FromReques" class="headerlink" title="FromReques"></a>FromReques<a class="article-anchor" href="#FromReques" aria-hidden="true"></a></h2><p>某些情况下，我们可能需要返回request中的值。比如添加一个用户，成功时，我们应当返回该用户在后端实际存储的信息给前端。所以我们需要调用<code>fromReques()</code>获取数据</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    response &#123;<br>        body([<br>                <span class="hljs-string">&quot;gender&quot;</span>: fromRequest().query(<span class="hljs-string">&#x27;gender&#x27;</span>),<br>                <span class="hljs-string">&quot;name&quot;</span>: fromRequest().body(<span class="hljs-string">&#x27;$.name&#x27;</span>)<br>        ])<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>
<p><code>fromRequest()</code>有以下一些方法：</p>
<ul>
<li>fromRequest().url(): 返回URL与query parameters.</li>
<li>fromRequest().query(String key): 返回第一个匹配到的query parameter值.</li>
<li>fromRequest().query(String key, int index): 返回第[index]个匹配到的query parameter值.</li>
<li>fromRequest().path(): 返回完整的url路径.</li>
<li>fromRequest().path(int index): 返回第[index]个url路径元素.</li>
<li>fromRequest().header(String key):返回第一个匹配到的header值.</li>
<li>fromRequest().header(String key, int index): 返回第[index]个匹配到的header值.</li>
<li>fromRequest().body(): 返回完整的body.</li>
<li>fromRequest().body(String jsonPath): 返回body中指定JSON路径的元素.</li>
</ul>
<h2 id="Body-1" class="article-heading"><a href="#Body-1" class="headerlink" title="Body"></a>Body<a class="article-anchor" href="#Body-1" aria-hidden="true"></a></h2><p>在Response中，我们可以使用其他的写法给body赋值，比如<code>&quot;&quot;&quot;</code>添加字符串</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    response &#123;<br>        body <span class="hljs-string">&quot;&quot;&quot;&#123;&quot;name&quot;:&quot;YaYaYa&quot;&#125;&quot;&quot;&quot;</span><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>又或者我们可以将它放在外部（如果样例非常大时，是个不错的方案）</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs groovy">Contract.make &#123;<br>    response &#123;<br>        body (file(<span class="hljs-string">&#x27;文件的相对路径，例如xxxx.json&#x27;</span>))<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<blockquote>
<p>这块内容在Request中也是可用的，但如果与动态属性结合会出错，可能是<a target="_blank" rel="noopener" href="https://github.com/spring-cloud/spring-cloud-contract/issues/1065">issue</a>吧。所以尽量不要在Request中使用这块内容。</p>
</blockquote>
<h1 id="一个比较完整示例" class="article-heading"><a href="#一个比较完整示例" class="headerlink" title="一个比较完整示例"></a>一个比较完整示例<a class="article-anchor" href="#一个比较完整示例" aria-hidden="true"></a></h1><p>在最后，给个比较完整的例子，将就着看吧~~</p>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs groovy"><span class="hljs-keyword">package</span> contracts<br><br><span class="hljs-keyword">import</span> org.springframework.cloud.contract.spec.Contract<br><br>Contract.make &#123;<br>    priority(<span class="hljs-number">1</span>)<br>    name(<span class="hljs-string">&#x27;第一个契约呀&#x27;</span>)<br>    description(<span class="hljs-string">&quot;&quot;&quot;</span><br><span class="hljs-string">        [运营]需要[去掉操作A]，目的是[减少流程与人工成本]，存在的问题[减少A操作，会使部分数据未记录（运营人员已知晓（见邮件‘邮件主题’））]</span><br><span class="hljs-string">    &quot;&quot;&quot;</span>)<br>    request &#123;<br>        method <span class="hljs-string">&#x27;PUT&#x27;</span><br>        urlPath($(c(regex(<span class="hljs-string">&#x27;^/user/.+&#x27;</span>)),p(<span class="hljs-string">&#x27;/user/1&#x27;</span>))) &#123;<br>            queryParameters &#123;<br>                parameter <span class="hljs-string">&#x27;limit&#x27;</span>: <span class="hljs-number">100</span><br>                parameter <span class="hljs-string">&#x27;filter&#x27;</span>: equalTo(<span class="hljs-string">&quot;email&quot;</span>)<br>                parameter <span class="hljs-string">&#x27;gender&#x27;</span>: value(consumer(containing(<span class="hljs-string">&quot;[mf]&quot;</span>)), producer(<span class="hljs-string">&#x27;mf&#x27;</span>))<br>                parameter <span class="hljs-string">&#x27;offset&#x27;</span>: value(consumer(matching(<span class="hljs-string">&quot;[0-9]+&quot;</span>)), producer(<span class="hljs-number">123</span>))<br>                parameter <span class="hljs-string">&#x27;loginStartsWith&#x27;</span>: value(consumer(notMatching(<span class="hljs-string">&quot;.&#123;0,2&#125;&quot;</span>)), producer(<span class="hljs-number">3</span>))<br>                parameter <span class="hljs-string">&#x27;uuid&#x27;</span>: $(anyUuid())<br>            &#125;<br>        &#125;<br>        headers &#123;<br>            contentType(applicationJson())<br>        &#125;<br>        body([<br>                <span class="hljs-symbol">name:</span> value(anyNonEmptyString(), producer(<span class="hljs-string">&quot;从入门到弃坑&quot;</span>)),<br>                <span class="hljs-symbol">price:</span> value(anyNumber(), producer(<span class="hljs-string">&quot;1&quot;</span>))<br>        ])<br>    &#125;<br>    response &#123;<br>        status OK()<br>        body([<br>                <span class="hljs-string">&quot;gender&quot;</span>: fromRequest().query(<span class="hljs-string">&#x27;gender&#x27;</span>),<br>                <span class="hljs-string">&quot;name&quot;</span>: fromRequest().body(<span class="hljs-string">&#x27;$.name&#x27;</span>)<br>        ])<br>        headers &#123;<br>            contentType(applicationJsonUtf8())<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

      
    </div>

    







<div>
  <ul class="post-copyright">
    <li class="post-copyright-author">
      <strong>本文作者： </strong>
      Mr.J
    </li>
    <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="https://jiangtj.com/articles/beechnut/spring-cloud-contract-dsl/">https://jiangtj.com/articles/beechnut/spring-cloud-contract-dsl/</a>
    <li class="post-copyright-license">
      <strong>版权声明： </strong>
      本博客所有文章除特别声明外，均采用 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="creative-commons" class="svg-inline--fa fa-creative-commons fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M245.83 214.87l-33.22 17.28c-9.43-19.58-25.24-19.93-27.46-19.93-22.13 0-33.22 14.61-33.22 43.84 0 23.57 9.21 43.84 33.22 43.84 14.47 0 24.65-7.09 30.57-21.26l30.55 15.5c-6.17 11.51-25.69 38.98-65.1 38.98-22.6 0-73.96-10.32-73.96-77.05 0-58.69 43-77.06 72.63-77.06 30.72-.01 52.7 11.95 65.99 35.86zm143.05 0l-32.78 17.28c-9.5-19.77-25.72-19.93-27.9-19.93-22.14 0-33.22 14.61-33.22 43.84 0 23.55 9.23 43.84 33.22 43.84 14.45 0 24.65-7.09 30.54-21.26l31 15.5c-2.1 3.75-21.39 38.98-65.09 38.98-22.69 0-73.96-9.87-73.96-77.05 0-58.67 42.97-77.06 72.63-77.06 30.71-.01 52.58 11.95 65.56 35.86zM247.56 8.05C104.74 8.05 0 123.11 0 256.05c0 138.49 113.6 248 247.56 248 129.93 0 248.44-100.87 248.44-248 0-137.87-106.62-248-248.44-248zm.87 450.81c-112.54 0-203.7-93.04-203.7-202.81 0-105.42 85.43-203.27 203.72-203.27 112.53 0 202.82 89.46 202.82 203.26-.01 121.69-99.68 202.82-202.84 202.82z"></path></svg>BY-NC-SA</a> 许可协议。转载请注明出处！    </li>
  </ul>
</div>


    
<div class="post-tags">
  
  <a href="/tags/Spring-Cloud/" rel="tag"># Spring Cloud</a>
  
  <a href="/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/" rel="tag"># 微服务</a>
  
</div>


    
    <div class="reward-container"><div>Buy me a cup of milkshake 🍨.</div><div class="reward-box"><div class="reward-box-card wechat">
  <img class="reward-image" src="/images/wechatpay.png" />
  
</div>
<div class="reward-box-card alipay">
  <img class="reward-image" src="/images/alipay.png" />
  
  <a class="reward-image-url" href="HTTPS://QR.ALIPAY.COM/FKX06416WJNHOWKMRQQFFE">点击跳转</a>
  
</div>
<div class="reward-box-card paypal">
  <img class="reward-image" src="/images/paypal.png" />
  
  <a class="reward-image-url" target="_blank" rel="noopener" href="https://www.paypal.me/jiangtj">https://www.paypal.me/jiangtj</a>
  
</div>
<div class="reward-box-card kofi">
  <img class="reward-image" src="/images/kofi.png" />
  
  <a class="reward-image-url" target="_blank" rel="noopener" href="https://ko-fi.com/jiangtj">https://ko-fi.com/jiangtj</a>
  
</div>
</div><div class="reward-group"><a class="reward-button wechat"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="weixin" class="svg-inline--fa fa-weixin fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M385.2 167.6c6.4 0 12.6.3 18.8 1.1C387.4 90.3 303.3 32 207.7 32 100.5 32 13 104.8 13 197.4c0 53.4 29.3 97.5 77.9 131.6l-19.3 58.6 68-34.1c24.4 4.8 43.8 9.7 68.2 9.7 6.2 0 12.1-.3 18.3-.8-4-12.9-6.2-26.6-6.2-40.8-.1-84.9 72.9-154 165.3-154zm-104.5-52.9c14.5 0 24.2 9.7 24.2 24.4 0 14.5-9.7 24.2-24.2 24.2-14.8 0-29.3-9.7-29.3-24.2.1-14.7 14.6-24.4 29.3-24.4zm-136.4 48.6c-14.5 0-29.3-9.7-29.3-24.2 0-14.8 14.8-24.4 29.3-24.4 14.8 0 24.4 9.7 24.4 24.4 0 14.6-9.6 24.2-24.4 24.2zM563 319.4c0-77.9-77.9-141.3-165.4-141.3-92.7 0-165.4 63.4-165.4 141.3S305 460.7 397.6 460.7c19.3 0 38.9-5.1 58.6-9.9l53.4 29.3-14.8-48.6C534 402.1 563 363.2 563 319.4zm-219.1-24.5c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.8 0 24.4 9.7 24.4 19.3 0 10-9.7 19.6-24.4 19.6zm107.1 0c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.5 0 24.4 9.7 24.4 19.3.1 10-9.9 19.6-24.4 19.6z"></path></svg></span></a><a class="reward-button alipay"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="alipay" class="svg-inline--fa fa-alipay fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M377.74 32H70.26C31.41 32 0 63.41 0 102.26v307.48C0 448.59 31.41 480 70.26 480h307.48c38.52 0 69.76-31.08 70.26-69.6-45.96-25.62-110.59-60.34-171.6-88.44-32.07 43.97-84.14 81-148.62 81-70.59 0-93.73-45.3-97.04-76.37-3.97-39.01 14.88-81.5 99.52-81.5 35.38 0 79.35 10.25 127.13 24.96 16.53-30.09 26.45-60.34 26.45-60.34h-178.2v-16.7h92.08v-31.24H88.28v-19.01h109.44V92.34h50.92v50.42h109.44v19.01H248.63v31.24h88.77s-15.21 46.62-38.35 90.92c48.93 16.7 100.01 36.04 148.62 52.74V102.26C447.83 63.57 416.43 32 377.74 32zM47.28 322.95c.99 20.17 10.25 53.73 69.93 53.73 52.07 0 92.58-39.68 117.87-72.9-44.63-18.68-84.48-31.41-109.44-31.41-67.45 0-79.35 33.06-78.36 50.58z"></path></svg></span></a><a class="reward-button paypal"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="paypal" class="svg-inline--fa fa-paypal fa-w-12 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M111.4 295.9c-3.5 19.2-17.4 108.7-21.5 134-.3 1.8-1 2.5-3 2.5H12.3c-7.6 0-13.1-6.6-12.1-13.9L58.8 46.6c1.5-9.6 10.1-16.9 20-16.9 152.3 0 165.1-3.7 204 11.4 60.1 23.3 65.6 79.5 44 140.3-21.5 62.6-72.5 89.5-140.1 90.3-43.4.7-69.5-7-75.3 24.2zM357.1 152c-1.8-1.3-2.5-1.8-3 1.3-2 11.4-5.1 22.5-8.8 33.6-39.9 113.8-150.5 103.9-204.5 103.9-6.1 0-10.1 3.3-10.9 9.4-22.6 140.4-27.1 169.7-27.1 169.7-1 7.1 3.5 12.9 10.6 12.9h63.5c8.6 0 15.7-6.3 17.4-14.9.7-5.4-1.1 6.1 14.4-91.3 4.6-22 14.3-19.7 29.3-19.7 71 0 126.4-28.8 142.9-112.3 6.5-34.8 4.6-71.4-23.8-92.6z"></path></svg></span></a><a class="reward-button kofi"><span><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="coffee" class="svg-inline--fa fa-coffee fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M192 384h192c53 0 96-43 96-96h32c70.6 0 128-57.4 128-128S582.6 32 512 32H120c-13.3 0-24 10.7-24 24v232c0 53 43 96 96 96zM512 96c35.3 0 64 28.7 64 64s-28.7 64-64 64h-32V96h32zm47.7 384H48.3c-47.6 0-61-64-36-64h583.3c25 0 11.8 64-35.9 64z"></path></svg></span></a></div></div>    

    
    <footer class="post-footer">
      
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/articles/beechnut/vue-cli-create/" rel="prev" title="一点点解析Vue CLI之Create">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="chevron-left" class="svg-inline--fa fa-chevron-left fa-w-10 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"></path></svg>
      一点点解析Vue CLI之Create
    </a></div>
      <div class="post-nav-item">
    <a href="/articles/beechnut/todo-an-new-theme/" rel="next" title="想写个自己的主题">
      想写个自己的主题
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="chevron-right" class="svg-inline--fa fa-chevron-right fa-w-10 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"></path></svg>
    </a></div>
    </div>    </footer>
    
  </div>
</article>
</div>
        </div>
        




<div class="card comment-body disqus active">
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = 'https://jiangtj.com/articles/beechnut/spring-cloud-contract-dsl/';;  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'articles/beechnut/spring-cloud-contract-dsl/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://jiangtj.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a target="_blank" rel="noopener" href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    </div>



      </main>

      <footer class="footer">
        <div>
          <div class="copyright">
  
  &copy; 
  <span>2021</span>
  <span class="with-love">
    <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="heart" class="svg-inline--fa fa-heart fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"></path></svg>
    <i class="[object Object]"></i>
  </span>
  <span class="author">Mr.J</span>
</div>

<div class="powered-by">
  由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> 强力驱动 v5.4.0
</div>

<span class="post-meta-divider">|</span>

<div class="theme-info">
  主题 – <a href="https://github.com/JiangTJ/hexo-theme-cake" class="theme-link" rel="noopener" target="_blank">Cake</a> v4.0.0
</div>

        </div>
      </footer>

    </div>

  </div>

  <div class="search-popup">
  <div class="search-header">
    <span class="search-icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg></span>
    <div class="search-input-container">
      <input autocomplete="off" autocorrect="off" autocapitalize="none" placeholder="搜索..."
        spellcheck="false" type="text" id="search-input">
    </div>
    <span class="popup-btn-close"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="times-circle" class="svg-inline--fa fa-times-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path></svg></span>
  </div>
  <div id="search-result"></div>
</div>
<div class="search-pop-overlay"></div>
<script id="ribbon" size="300" alpha="0.6" zIndex="-1" src="//cdn.jsdelivr.net/gh/theme-next/theme-next-canvas-ribbon@1/canvas-ribbon.js"></script><script>
((window.gitter = {}).chat = {}).options = {
  room: 'jiangtj/Lobby',
  activationElement: false
};
</script><script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script><script>
  (function () {
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
  })();
</script>
<div class="moon-menu">
  <div class="moon-menu-items">
    
    <div id="moon-menu-item-back2bottom" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-down" class="svg-inline--fa fa-chevron-down fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path></svg>    </div>
    
    <div id="moon-menu-item-back2top" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-up" class="svg-inline--fa fa-chevron-up fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z"></path></svg>    </div>
    
    <div id="moon-menu-item-chat" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="comments" class="svg-inline--fa fa-comments fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M416 192c0-88.4-93.1-160-208-160S0 103.6 0 192c0 34.3 14.1 65.9 38 92-13.4 30.2-35.5 54.2-35.8 54.5-2.2 2.3-2.8 5.7-1.5 8.7S4.8 352 8 352c36.6 0 66.9-12.3 88.7-25 32.2 15.7 70.3 25 111.3 25 114.9 0 208-71.6 208-160zm122 220c23.9-26 38-57.7 38-92 0-66.9-53.5-124.2-129.3-148.1.9 6.6 1.3 13.3 1.3 20.1 0 105.9-107.7 192-240 192-10.8 0-21.3-.8-31.7-1.9C207.8 439.6 281.8 480 368 480c41 0 79.1-9.2 111.3-25 21.8 12.7 52.1 25 88.7 25 3.2 0 6.1-1.9 7.3-4.8 1.3-2.9.7-6.3-1.5-8.7-.3-.3-22.4-24.2-35.8-54.5z"></path></svg>    </div>
    
    <div id="moon-menu-item-switch_color" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="adjust" class="svg-inline--fa fa-adjust fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M8 256c0 136.966 111.033 248 248 248s248-111.034 248-248S392.966 8 256 8 8 119.033 8 256zm248 184V72c101.705 0 184 82.311 184 184 0 101.705-82.311 184-184 184z"></path></svg>    </div>
    
  </div>
  <div class="moon-menu-button">
    <svg class="moon-menu-bg">
      <circle class="moon-menu-cricle" cx="50%" cy="50%" r="44%"></circle>
      <circle class="moon-menu-border" cx="50%" cy="50%" r="48%"></circle>
    </svg>
    <div class="moon-menu-content">
      <div class="moon-menu-icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="ellipsis-v" class="svg-inline--fa fa-ellipsis-v fa-w-6 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"><path fill="currentColor" d="M96 184c39.8 0 72 32.2 72 72s-32.2 72-72 72-72-32.2-72-72 32.2-72 72-72zM24 80c0 39.8 32.2 72 72 72s72-32.2 72-72S135.8 8 96 8 24 40.2 24 80zm0 352c0 39.8 32.2 72 72 72s72-32.2 72-72-32.2-72-72-72-72 32.2-72 72z"></path></svg></div>
      <div class="moon-menu-text"></div>
    </div>
  </div>
</div><script src="/js/injector.js"></script>
<!-- hexo injector body_end start --><script>
// Check that service workers are supported
if ('serviceWorker' in navigator) {
  // Use the window load event to keep the page load performant
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  });
}
</script>
<!-- hexo injector body_end end --></body>

</html>
